import {
  useEffect,
  type RefObject,
} from 'react'
import { useTouch } from './use-touch'
import { getScrollParent } from '../utils/get-scroll-parent'
import { supportsPassive } from '../utils/supports-passive'

const BODY_LOCK_CLASS = 'rt-overflow-hidden'

let totalLockCount = 0

function getScrollableElement(el: HTMLElement | null) {
  let current = el?.parentElement

  while (current) {
    if (current.clientHeight < current.scrollHeight) {
      return current
    }
    current = current.parentElement
  }

  return null
}

export const useLockScroll = (
  rootRef: RefObject<HTMLElement>,
  shouldLock: boolean | 'strict',
): void => {
  const touch = useTouch()

  const onTouchMove = (event: TouchEvent) => {
    touch.move(event)
    const direction = touch.deltaY.current > 0 ? '10' : '01'
    const el = getScrollParent(
      event.target as Element,
      rootRef.current,
    ) as HTMLElement

    if (!el) return

    // This has perf cost but we have to compatible with iOS 12
    if (shouldLock === 'strict') {
      const scrollableParent = getScrollableElement(event.target as HTMLElement)
      if (
        scrollableParent === document.body
        || scrollableParent === document.documentElement
      ) {
        event.preventDefault()
        return
      }
    }

    const { scrollHeight, offsetHeight, scrollTop } = el
    const { height } = el.getBoundingClientRect()

    /**
     * status === '00' - 当前元素不可滚动
     * status === '01' - 当前元素可滚动, 滚动条在顶部
     * status === '10' - 当前元素可滚动, 滚动条在底部
     * status === '11' - default. 当前元素可滚动, 且滚动条不在顶部也不在底部
     */
    let status = '11'

    if (scrollTop === 0) {
      // 滚动条在顶部时，需要判断是当前元素不可以滚动还是可以滚动但是未进行任何滚动
      status = offsetHeight >= scrollHeight ? '00' : '01'
    } else if (scrollHeight <= Math.round(height + scrollTop)) {
      status = '10'
    }

    // “&”的运算方法：两个数值的个位分别相与，同时为1才得1，只要一个为0就为0。
    // 1.1 完成上述的判断后，如果 status === 11 表示当前元素可滚动并且滚动条不在顶部也不在底部（即在中间），表示 touchMove 事件不应该阻止元素滚动（当前滚动为正常现象）
    // 1.2 同时 touch.isVertical() 明确确保是垂直方向的拖动
    // 2.1 当 status 为 '00' 时， 0 & 任何数都是 0.自然 !(parseInt(status, 2) & parseInt(direction, 2)) 会变为 true，需要阻止意外的滚动行为。
    // 2.2 当 status 为 '01' 时，此时当用户从上往下拖动时，需要阻止意外的滚动行为发生。否则，则不需要阻止正常滚动。 自然 status === '01' ，direction === 10（从上往下拖动），!(parseInt(status, 2) & parseInt(direction, 2)) 为 true 需要进行阻止默认滚动行为。(1 & 1 为 1 ，1 & 2 为 0)
    // 2.3 当 status 为 '10' 时，自然对于从下往上拖动时 direction 为 01 时也应该阻止，所以 (2 & 1 = 0) 自然 !(parseInt(status, 2) & parseInt(direction, 2)) 为 true，同样会进入if语句阻止意外滚动。
    if (
      status !== '11'
      && touch.isVertical()
      // eslint-disable-next-line no-bitwise
      && !(parseInt(status, 2) & parseInt(direction, 2))
    ) {
      if (event.cancelable) {
        event.preventDefault()
      }
    }
  }

  const lock = () => {
    document.addEventListener('touchstart', touch.start)
    document.addEventListener(
      'touchmove',
      onTouchMove,
      supportsPassive ? { passive: false } : false,
    )

    if (!totalLockCount) {
      document.body.classList.add(BODY_LOCK_CLASS)
    }

    totalLockCount += 1
  }

  const unLock = () => {
    if (totalLockCount) {
      document.removeEventListener('touchstart', touch.start)
      document.removeEventListener('touchmove', onTouchMove)

      totalLockCount -= 1

      if (!totalLockCount) {
        document.body.classList.remove(BODY_LOCK_CLASS)
      }
    }
  }

  useEffect(() => {
    if (shouldLock) {
      lock()
      return () => {
        unLock()
      }
    }
    return () => {
      // void()
    }
  }, [shouldLock])
}
